package com.cloud.retry.aop;

import com.alibaba.fastjson.JSON;
import com.cloud.retry.annotation.LogApi;
import com.cloud.retry.entity.LogEntity;
import com.cloud.retry.service.LogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;

@Slf4j
@Aspect
@Component
public class LogAspect {

    //key是接口名称   value是requestMapping
    public static Map<String, HandlerMethod> map = new HashMap();

    @Autowired
    private LogService logService;
    @Autowired
    private ApplicationContext applicationContext;

    @Pointcut("@annotation(com.cloud.retry.annotation.LogApi)")
    public void logApi() {
    }

    /**
     * 把需要重试的放到map中
     */
    @PostConstruct
    public void setHandlerMethodMap() {

        RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = handlerMapping.getHandlerMethods();

        for (Map.Entry<RequestMappingInfo, HandlerMethod> handlerMethodEntry : handlerMethodMap.entrySet()) {
            HandlerMethod handlerMethod = handlerMethodEntry.getValue();
            LogApi logApi = handlerMethod.getMethodAnnotation(LogApi.class);
            if (Objects.nonNull(logApi)) {
                map.put(logApi.value(), handlerMethod);
            }
        }
    }

    @Around("logApi()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogApi logApi = method.getAnnotation(LogApi.class);
        LogEntity entity = new LogEntity();
        if (joinPoint.getArgs() != null && joinPoint.getArgs().length != 0) {
            Object firstArg = joinPoint.getArgs()[0];
            entity.setRequestBody(JSON.toJSONString(firstArg));
        }
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        Objects.requireNonNull(requestAttributes, "requestAttributes is null");
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        entity = createLog(logApi.value(), entity, request);
        Object responseObj = joinPoint.proceed();
        entity.setResponseBody(JSON.toJSONString(responseObj));
        try {
            logService.updateById(entity);
        } catch (Exception e) {
            log.error("更新Log日志时发生错误：" + e.getMessage());
        }
        return responseObj;
    }


    /**
     * 记录日志
     */
    private LogEntity createLog(String api, LogEntity entity, HttpServletRequest request) {

        String sourceApplicationName = null;
        // 获取请求头
        Enumeration<String> enumeration = request.getHeaderNames();
        StringBuffer headers = new StringBuffer();
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            String value = request.getHeader(name);
            headers.append(name + ":" + value).append(",");
            if ("source-name".equals(name) && StringUtils.isNotBlank(value)) {
                sourceApplicationName = value;
            }
        }
        //日志记录的类型：通知，支付，退款，提现
        entity.setType(api);
        entity.setSourceName(sourceApplicationName);
        entity.setRequestMethod(request.getMethod());
        entity.setRequestUrl(request.getRequestURL().toString());
        entity.setRequestPath(request.getRequestURI());
        entity.setRequestHeaders(headers.toString());
        entity.setRequestTime(new Date());
        entity.setCrtTime(new Date());
        entity.setUpdTime(new Date());
        try {
            logService.save(entity);
        } catch (Exception e) {
            log.error("插入Log日志时发生错误：" + e.getMessage());
        }
        return entity;
    }
}